/*==============================================================================
            Copyright (c) 2013 Moritz Hader, Marcel Schrder
            All Rights Reserved.

	@file  HeimdallLinkedList.h

	@brief This header provides the class-definitions of the HeimdallLinkedList.
	The linked list serves as a container for Heimdall3DObjects that
	are passed down from the java-code. It will maintain a consecutive list of
	elements that can be arbitrary extended or reduced without the need of
	copying and resizing an array.
   
   This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
================================================================================*/
#include <engine/HeimdallObjectContainer.h>
#include <engine/HeimdallNative3DObject.h>
#include <egl/HeimdallGLUtilities.h>

HeimdallObjectContainer::HeimdallObjectContainer(){

}

HeimdallObjectContainer::~HeimdallObjectContainer(){
	//Delete everything...
}

void HeimdallObjectContainer::addObject(HeimdallNative3DObject* obj){

	int index = this->nextAvailableIndex();

	__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Creating Object-Entry with ID: %d", index);

	if(index > 0){

		obj->setIndex(index);

		if(this->firstElement == NULL){
			this->firstElement = obj;
		}else{
			this->lastElement->setNextElement(obj);
			obj->setPreviousElement(this->lastElement);
			//this->lastElement = obj;
		}

		this->lastElement = obj;
	}
}

void HeimdallObjectContainer::deleteObject(int index){

	__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Deleting Object-Entry with ID: %d", index);

	HeimdallNative3DObject* buffer = this->firstElement;

	while(buffer != NULL){

		/*
		 *If the searched index equals the selected index, we can start to delete
		 *the element
		 */
		if(buffer->getIndex() == index){
			//Remove the element form the list

			if(buffer->getNextElement() != NULL){

				//The selected element has a successor
				if(buffer->getPreviousElement() == NULL){

					//There is no previous element, thus the selected element must be the firstElement
					this->firstElement = (HeimdallNative3DObject*)buffer->getNextElement();
					delete buffer;
				}
				else{
					//There is a previous element.

					//Set the successor of the previous element to the successor of the selected element.
					buffer->getPreviousElement()->setNextElement(buffer->getNextElement());

					//Set the predecessor of the successor of the selected element to the predecessor of the selected element
					buffer->getNextElement()->setPreviousElement(buffer->getPreviousElement());

					delete buffer;
				}

			}else{
				//There is no successor

				if(buffer->getPreviousElement() == NULL){
					//There is no predecessor and no successor, thus, we are firstElement and the list is of length 1
					delete buffer;
					this->firstElement = NULL;

				}else{
					//There is no successor but a predecessor, we must be lastElement
					this->lastElement = (HeimdallNative3DObject*)buffer->getPreviousElement();
					this->lastElement->setNextElement(NULL);
					delete buffer;
				}
			}

			break;
		}

		//Load the next element
		buffer = (HeimdallNative3DObject*)buffer->getNextElement();
	}
}

HeimdallNative3DObject* HeimdallObjectContainer::getObject(int index){

	HeimdallNative3DObject* buffer = this->firstElement;

	while(buffer != NULL){

		if(buffer->getIndex() == index){
			return buffer;
		}

		//Load the next element
		buffer = (HeimdallNative3DObject*)buffer->getNextElement();
	}

	return NULL;
}

/**
 * Find the next available index by searching the existing objects.
 * NOTE: This method is not very efficient, as it traverses every element.
 */
int HeimdallObjectContainer::nextAvailableIndex(){
	int i = 1; //1 is the first possible index;

	if(this->firstElement == NULL){
		this->highestKey = 1;
		return i;
	}

	if(this->highestKey >= maxInteger){

		//Reset the highest key, as we are going to reinstanciate it in the loop
		this->highestKey = 1;

		HeimdallNative3DObject* buffer = this->firstElement;

		while(buffer != NULL){

			//As this part is only executed when there are no more available keys, we may want to look for the recent border
			if(buffer->getIndex() > this->highestKey){
				this->highestKey = buffer->getIndex();
			}

			if(buffer->getIndex() == i){
				i++;
				buffer = this->firstElement;
			}else{
				//Load the next element
				buffer = (HeimdallNative3DObject*)buffer->getNextElement();
			}
		}

	}else{
		this->highestKey++;
		i = this->highestKey;
	}

	__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Next ID: %d generated from cachesize %d", i,maxInteger);

	return i;
}


HeimdallNative3DObject* HeimdallObjectContainer::getFirstElement(){
	return this->firstElement;
}

void HeimdallObjectContainer::purgeList(){

HeimdallNative3DObject* buffer = this->firstElement;

	while(buffer != NULL){

		this->firstElement = (HeimdallNative3DObject*)buffer->getNextElement();

		delete buffer;

		//Load the next element
		buffer = this->firstElement;
	}
}
